/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#ifndef mozilla_image_FrameAnimator_h#define mozilla_image_FrameAnimator_h#include"mozilla/Maybe.h"#include"mozilla/MemoryReporting.h"#include"mozilla/TimeStamp.h"#include"gfxTypes.h"#include"imgFrame.h"#include"nsCOMPtr.h"#include"nsRect.h"#include"SurfaceCache.h"#include"gfxPrefs.h"namespacemozilla{namespaceimage{classRasterImage;classDrawableSurface;classAnimationState{public:explicitAnimationState(uint16_taAnimationMode):mFrameCount(0),mCurrentAnimationFrameIndex(0),mLoopRemainingCount(-1),mLoopCount(-1),mFirstFrameTimeout(FrameTimeout::FromRawMilliseconds(0)),mAnimationMode(aAnimationMode),mHasBeenDecoded(false),mIsCurrentlyDecoded(false),mCompositedFrameInvalid(false),mDiscarded(false){}/** * Call this whenever a decode completes, a decode starts, or the image is * discarded. It will update the internal state. Specifically mDiscarded, * mCompositedFrameInvalid, and mIsCurrentlyDecoded. If aAllowInvalidation * is true then returns a rect to invalidate. */constgfx::IntRectUpdateState(boolaAnimationFinished,RasterImage*aImage,constgfx::IntSize&aSize,boolaAllowInvalidation=true);private:constgfx::IntRectUpdateStateInternal(LookupResult&aResult,boolaAnimationFinished,constgfx::IntSize&aSize,boolaAllowInvalidation=true);public:/** * Call when a decode of this image has been completed. */voidNotifyDecodeComplete();/** * Returns true if this image has been fully decoded before. */boolGetHasBeenDecoded(){returnmHasBeenDecoded;}/** * Returns true if this image has been discarded and a decoded has not yet * been created to redecode it. */boolIsDiscarded(){returnmDiscarded;}/** * Sets the composited frame as valid or invalid. */voidSetCompositedFrameInvalid(boolaInvalid){MOZ_ASSERT(!aInvalid||gfxPrefs::ImageMemAnimatedDiscardable());mCompositedFrameInvalid=aInvalid;}/** * Returns whether the composited frame is valid to draw to the screen. */boolGetCompositedFrameInvalid(){returnmCompositedFrameInvalid;}/** * Returns whether the image is currently full decoded.. */boolGetIsCurrentlyDecoded(){returnmIsCurrentlyDecoded;}/** * Call when you need to re-start animating. Ensures we start from the first * frame. */voidResetAnimation();/** * The animation mode of the image. * * Constants defined in imgIContainer.idl. */voidSetAnimationMode(uint16_taAnimationMode);/// Update the number of frames of animation this image is known to have.voidUpdateKnownFrameCount(uint32_taFrameCount);/// @return the number of frames of animation we know about so far.uint32_tKnownFrameCount()const{returnmFrameCount;}/// @return the number of frames this animation has, if we know for sure./// (In other words, if decoding is finished.) Otherwise, returns Nothing().Maybe<uint32_t>FrameCount()const;/** * Get or set the area of the image to invalidate when we loop around to the * first frame. */voidSetFirstFrameRefreshArea(constgfx::IntRect&aRefreshArea);gfx::IntRectFirstFrameRefreshArea()const{returnmFirstFrameRefreshArea;}/** * If the animation frame time has not yet been set, set it to * TimeStamp::Now(). */voidInitAnimationFrameTimeIfNecessary();/** * Set the animation frame time to @aTime. */voidSetAnimationFrameTime(constTimeStamp&aTime);/** * The current frame we're on, from 0 to (numFrames - 1). */uint32_tGetCurrentAnimationFrameIndex()const;/* * Set number of times to loop the image. * @note -1 means loop forever. */voidSetLoopCount(int32_taLoopCount){mLoopCount=aLoopCount;}int32_tLoopCount()const{returnmLoopCount;}/// Set the @aLength of a single loop through this image.voidSetLoopLength(FrameTimeoutaLength){mLoopLength=Some(aLength);}/** * @return the length of a single loop of this image. If this image is not * finished decoding, is not animated, or it is animated but does not loop, * returns FrameTimeout::Forever(). */FrameTimeoutLoopLength()const;/* * Get or set the timeout for the first frame. This is used to allow animation * scheduling even before a full decode runs for this image. */voidSetFirstFrameTimeout(FrameTimeoutaTimeout){mFirstFrameTimeout=aTimeout;}FrameTimeoutFirstFrameTimeout()const{returnmFirstFrameTimeout;}private:friendclassFrameAnimator;//! Area of the first frame that needs to be redrawn on subsequent loops.gfx::IntRectmFirstFrameRefreshArea;//! the time that the animation advanced to the current frameTimeStampmCurrentAnimationFrameTime;//! The number of frames of animation this image has.uint32_tmFrameCount;//! The current frame index we're on, in the range [0, mFrameCount).uint32_tmCurrentAnimationFrameIndex;//! number of loops remaining before animation stops (-1 no stop)int32_tmLoopRemainingCount;//! The total number of loops for the image.int32_tmLoopCount;//! The length of a single loop through this image.Maybe<FrameTimeout>mLoopLength;//! The timeout for the first frame of this image.FrameTimeoutmFirstFrameTimeout;//! The animation mode of this image. Constants defined in imgIContainer.uint16_tmAnimationMode;/** * The following four bools (mHasBeenDecoded, mIsCurrentlyDecoded, * mCompositedFrameInvalid, mDiscarded) track the state of the image with * regards to decoding. They all start out false, including mDiscarded, * because we want to treat being discarded differently from "not yet decoded * for the first time". * * (When we are decoding the image for the first time we want to show the * image at the speed of data coming in from the network or the speed * specified in the image file, whichever is slower. But when redecoding we * want to show nothing until the frame for the current time has been * decoded. The prevents the user from seeing the image "fast forward" * to the expected spot.) * * When the image is decoded for the first time mHasBeenDecoded and * mIsCurrentlyDecoded get set to true. When the image is discarded * mIsCurrentlyDecoded gets set to false, and mCompositedFrameInvalid * & mDiscarded get set to true. When we create a decoder to redecode the * image mDiscarded gets set to false. mCompositedFrameInvalid gets set to * false when we are able to advance to the frame that should be showing * for the current time. mIsCurrentlyDecoded gets set to true when the * redecode finishes. *///! Whether this image has been decoded at least once.boolmHasBeenDecoded;//! Whether this image is currently fully decoded.boolmIsCurrentlyDecoded;//! Whether the composited frame is valid to draw to the screen, note that//! the composited frame can exist and be filled with image data but not//! valid to draw to the screen.boolmCompositedFrameInvalid;//! Whether this image is currently discarded. Only set to true after the//! image has been decoded at least once.boolmDiscarded;};/** * RefreshResult is used to let callers know how the state of the animation * changed during a call to FrameAnimator::RequestRefresh(). */structRefreshResult{RefreshResult():mFrameAdvanced(false),mAnimationFinished(false){}/// Merges another RefreshResult's changes into this RefreshResult.voidAccumulate(constRefreshResult&aOther){mFrameAdvanced=mFrameAdvanced||aOther.mFrameAdvanced;mAnimationFinished=mAnimationFinished||aOther.mAnimationFinished;mDirtyRect=mDirtyRect.Union(aOther.mDirtyRect);}// The region of the image that has changed.gfx::IntRectmDirtyRect;// If true, we changed frames at least once. Note that, due to looping, we// could still have ended up on the same frame!boolmFrameAdvanced:1;// Whether the animation has finished playing.boolmAnimationFinished:1;};classFrameAnimator{public:FrameAnimator(RasterImage*aImage,constgfx::IntSize&aSize):mImage(aImage),mSize(aSize),mLastCompositedFrameIndex(-1){MOZ_COUNT_CTOR(FrameAnimator);}~FrameAnimator(){MOZ_COUNT_DTOR(FrameAnimator);}/** * Re-evaluate what frame we're supposed to be on, and do whatever blending * is necessary to get us to that frame. * * Returns the result of that blending, including whether the current frame * changed and what the resulting dirty rectangle is. */RefreshResultRequestRefresh(AnimationState&aState,constTimeStamp&aTime,boolaAnimationFinished);/** * Get the full frame for the current frame of the animation (it may or may * not have required compositing). It may not be available because it hasn't * been decoded yet, in which case we return an empty LookupResult. */LookupResultGetCompositedFrame(AnimationState&aState);/** * Collect an accounting of the memory occupied by the compositing surfaces we * use during animation playback. All of the actual animation frames are * stored in the SurfaceCache, so we don't need to report them here. */voidCollectSizeOfCompositingSurfaces(nsTArray<SurfaceMemoryCounter>&aCounters,MallocSizeOfaMallocSizeOf)const;private:// methods/** * Advances the animation. Typically, this will advance a single frame, but it * may advance multiple frames. This may happen if we have infrequently * "ticking" refresh drivers (e.g. in background tabs), or extremely short- * lived animation frames. * * @param aTime the time that the animation should advance to. This will * typically be <= TimeStamp::Now(). * * @returns a RefreshResult that shows whether the frame was successfully * advanced, and its resulting dirty rect. */RefreshResultAdvanceFrame(AnimationState&aState,DrawableSurface&aFrames,TimeStampaTime);/** * Get the @aIndex-th frame in the frame index, ignoring results of blending. */RawAccessFrameRefGetRawFrame(DrawableSurface&aFrames,uint32_taFrameNum)const;/// @return the given frame's timeout if it is availableMaybe<FrameTimeout>GetTimeoutForFrame(AnimationState&aState,DrawableSurface&aFrames,uint32_taFrameNum)const;/** * Get the time the frame we're currently displaying is supposed to end. * * In the error case (like if the requested frame is not currently * decoded), returns None(). */Maybe<TimeStamp>GetCurrentImgFrameEndTime(AnimationState&aState,DrawableSurface&aFrames)const;boolDoBlend(DrawableSurface&aFrames,gfx::IntRect*aDirtyRect,uint32_taPrevFrameIndex,uint32_taNextFrameIndex);/** Clears an area of <aFrame> with transparent black. * * @param aFrameData Target Frame data * @param aFrameRect The rectangle of the data pointed ot by aFrameData * * @note Does also clears the transparency mask */staticvoidClearFrame(uint8_t*aFrameData,constgfx::IntRect&aFrameRect);//! @overloadstaticvoidClearFrame(uint8_t*aFrameData,constgfx::IntRect&aFrameRect,constgfx::IntRect&aRectToClear);//! Copy one frame's image and mask into anotherstaticboolCopyFrameImage(constuint8_t*aDataSrc,constgfx::IntRect&aRectSrc,uint8_t*aDataDest,constgfx::IntRect&aRectDest);/** * Draws one frame's image to into another, at the position specified by * aSrcRect. * * @aSrcData the raw data of the current frame being drawn * @aSrcRect the size of the source frame, and the position of that frame in * the composition frame * @aSrcPaletteLength the length (in bytes) of the palette at the beginning * of the source data (0 if image is not paletted) * @aSrcHasAlpha whether the source data represents an image with alpha * @aDstPixels the raw data of the composition frame where the current frame * is drawn into (32-bit ARGB) * @aDstRect the size of the composition frame * @aBlendMethod the blend method for how to blend src on the composition * frame. */staticnsresultDrawFrameTo(constuint8_t*aSrcData,constgfx::IntRect&aSrcRect,uint32_taSrcPaletteLength,boolaSrcHasAlpha,uint8_t*aDstPixels,constgfx::IntRect&aDstRect,BlendMethodaBlendMethod,constMaybe<gfx::IntRect>&aBlendRect);private:// data//! A weak pointer to our owning image.RasterImage*mImage;//! The intrinsic size of the image.gfx::IntSizemSize;/** For managing blending of frames * * Some animations will use the compositingFrame to composite images * and just hand this back to the caller when it is time to draw the frame. * NOTE: When clearing compositingFrame, remember to set * lastCompositedFrameIndex to -1. Code assume that if * lastCompositedFrameIndex >= 0 then compositingFrame exists. */RawAccessFrameRefmCompositingFrame;/** the previous composited frame, for DISPOSE_RESTORE_PREVIOUS * * The Previous Frame (all frames composited up to the current) needs to be * stored in cases where the image specifies it wants the last frame back * when it's done with the current frame. */RawAccessFrameRefmCompositingPrevFrame;//! Track the last composited frame for Optimizations (See DoComposite code)int32_tmLastCompositedFrameIndex;};}// namespace image}// namespace mozilla#endif // mozilla_image_FrameAnimator_h